#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <cstring>
#include <strings.h>
#include <cstdlib>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cassert>
#include <ctype.h>
#include <fstream>
using namespace std;

#define CRLF "\r\n"
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define HOME_PAGE "index.html"
#define ROOT_PATH "wwwroot"

std::string getPath(std::string http_request)
{
    std::size_t pos =http_request.find(CRLF);
    if(pos == std::string::npos)  return "";
    std::string request_line = http_request.substr(0,pos);

    std::size_t first = request_line.find(SPACE);
    if(pos == std::string::npos)  return "";
    std::size_t second = request_line.rfind(SPACE);
    if(pos == std::string::npos)  return "";

    std::string path = request_line.substr(first+SPACE_LEN, second - (first+SPACE_LEN));
    if(path.size() == 1 && path[0] == '/') path += HOME_PAGE;
    return path;
}

std::string readFile(const std::string& recource)
{
    std::ifstream in(recource,std::ifstream::binary);
    if(!in.is_open()) return "404";
    std::string line;
    std::string content;
    while(std::getline(in,line))
    {
        content+=line;
    }

    in.close();
    return content;
}

void handlerHttpRequest(int sock)
{
    char buffer[10240];
    ssize_t s= read(sock,buffer,sizeof(buffer));
    if(s>0)
    {
        std::cout<<buffer;
    
    }

    std::string path = getPath(buffer);
    //std::cout<<path<<std::endl;
    std::string recource = ROOT_PATH;
    recource+=path;
    std::cout<<recource<<std::endl;

    std::string html = readFile(recource);
    //开始响应
    std::string response;
    response = "HTTP/1.0 200 OK\r\n";
    response += "Content-Type: text/html\r\n";
    response += ("Content-Length: " + std::to_string(html.size()) + "\r\n");
    response += "\r\n";
    response += html;
    
    send(sock,response.c_str(),response.size(),0);

}

class ServerTcp
{
public:
    ServerTcp(uint16_t port, const std::string &ip = "")
        : port_(port), ip_(ip), listenSock_(-1)
    {
        quit_ = false;
    }
    ~ServerTcp()
    {
        if (listenSock_ >= 0)
            close(listenSock_);
    }

public:
    void init()
    {
        // 1.创建套接字
        listenSock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listenSock_ < 0)
        {
            exit(1);
        }
        // 2.bind绑定
        // 2.1 填充服务器信息
        struct sockaddr_in local;         // 用户栈
        memset(&local, 0, sizeof(local)); // 清空
        local.sin_family = PF_INET;       // 进行网络通信
        local.sin_port = htons(port_);
        ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY)
                    : (inet_aton(ip_.c_str(), &local.sin_addr));
        // 2.2 将本地socket信息写入sock_对应的内核区域
        if (bind(listenSock_, (const struct sockaddr *)&local, sizeof local) < 0)
        {
            exit(2);
        }

        // 3.设置listen(监听状态)
        // 为何要监听呢？因为tcp是面向连接的!
        // 要被连接 服务器必须一直等待被别人连接
        if (listen(listenSock_, 5 /*后面再说*/) < 0) // == -1
        {
            exit(3);
        }
        // 允许别人来连接我了
    }


    void start()
    {
        while (!quit_)
        {

            // signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信号
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 4.获取链接 accept的返回值是一个新得socket fd ??
            // 4.1  serviceSock:给用户提供新的socket服务
            // 返回值的socket的主要是为用户提供网络服务的socket,主要是进行io
            int serviceSock = accept(listenSock_, (struct sockaddr *)&peer, &len);
            if (serviceSock < 0)
            {
                // 获取链接失败
                continue;
            }
        
            // 5.1 v2版本 方案二 -- 多进程版本  也是可以的
            //爷爷进程
            pid_t id = fork();
            if(id == 0)
            {
                //爸爸进程
                close(listenSock_);//建议
                // 又进行了一次fork
                // 如果fork成功 爸爸进程直接退出了
                if(fork()>0) exit(0);
                // 孙子进程就是没有爸爸了 -- 孙子进程就成了孤儿进程 会被系统领养 回收问题就会交给系统回收
                handlerHttpRequest(serviceSock);
                exit(0);
            }
            //父进程
            close(serviceSock);//这一步一定要做的!
            //爸爸进程直接终止，立马得到退出码，释放僵尸进程状态
            pid_t ret = waitpid(id,nullptr,0);//就用阻塞式
            assert(ret >0 );
            (void) ret;

        }
    }

    bool quitServer()
    {
        quit_ = true;
        return true;
    }

private:
    int listenSock_;
    uint16_t port_;
    std::string ip_;
    // 安全退出
    bool quit_;
};

static void Usage(std::string proc)
{
    std::cerr << "Usage:\n\t" << proc << " port ip" << std::endl;
    std::cerr << "example:\n\t" << proc << " 8080 \n"
              << std::endl;
}

// ./ServerTcp local_port local_ip
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(4);
    }
    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
        ip = argv[2];
    ServerTcp svr(port, ip);
    svr.init();
    svr.start();

    return 0;
}